/*********************************************************/
/* TAUCS                                                 */
/* Author: Sivan Toledo                                  */
/*********************************************************/

#include "taucs.h"

#ifndef TAUCS_CONFIG_TIMING
void taucs_profile_report() {}
double taucs_wtime() { return 0.0; }
double taucs_ctime() { return 0.0; }
#else

#ifdef TAUCS_CONFIG_PROFILING

struct taucs_profile_st 
taucs_profile[] = {
  { "MULTILU: dense factor",           taucs_profile_multilu_dense_fctr   , 0, 0, 0, -1, -1 },
  { "MULTILU: dense solve",            taucs_profile_multilu_dense_solve  , 0, 0, 0, -1, -1 },
  { "MULTILU: assemble factor",        taucs_profile_multilu_cnv_lu       , 0, 0, 0, -1, -1 },
  { "MULTILU: compress",               taucs_profile_multilu_compress     , 0, 0, 0, -1, -1 },
  { "MULTILU: focus rows",             taucs_profile_multilu_focus_rows   , 0, 0, 0, -1, -1 },
  { "MULTILU: focus columns",          taucs_profile_multilu_focus_cols   , 0, 0, 0, -1, -1 },
  { "MULTILU: align add",              taucs_profile_multilu_align_add    , 0, 0, 0, -1, -1 },
  { "MULTILU: degrees",                taucs_profile_multilu_degrees      , 0, 0, 0, -1, -1 },
  { "MULTILU: preorder columns",       taucs_profile_multilu_preorder     , 0, 0, 0, -1, -1 },
  { "MULTILU: symbolic analysis",      taucs_profile_multilu_sym_anlz     , 0, 0, 0, -1, -1 },
  { "MULTILU: compress factor",        taucs_profile_multilu_compress_fctr, 0, 0, 0, -1, -1 },

  { "MULTIQR: dense factor",           taucs_profile_multiqr_dense_fctr   , 0, 0, 0, -1, -1 },
  { "MULTIQR: dense solve",            taucs_profile_multiqr_dense_solve  , 0, 0, 0, -1, -1 },
  { "MULTIQR: apply Q'",               taucs_profile_multiqr_apply_Qt     , 0, 0, 0, -1, -1 },
  { "MULTIQR: symbolic analysis",      taucs_profile_multiqr_sym_anlz     , 0, 0, 0, -1, -1 },
  { "MULTIQR: focus front",            taucs_profile_multiqr_focus_front  , 0, 0, 0, -1, -1 },
  { "MULTIQR: discard Y",              taucs_profile_multiqr_discard_y    , 0, 0, 0, -1, -1 },

  { 0, -1, 0, 0, 0, -1, -1 }
};

void taucs_profile_report()
{
  int i;
  
  taucs_printf("Profile information for profiled tasks:\n");
  taucs_printf("=======================================\n");
  for(i = 0; taucs_profile[i].label ; i++) {
    /*taucs_printf("%-35s   %.3f (%.3f) sec (%.3f%% [%.3f%%])\n", */
    if (taucs_profile[i].n)
      taucs_printf("%-35s   %.3f (%.3f) sec %6d times\n",
		   taucs_profile[i].label,
		   taucs_profile[i].wtime,
		   taucs_profile[i].ctime,
		   taucs_profile[i].n);
    taucs_profile[i].n = 0;
    taucs_profile[i].wtime = 0;
    taucs_profile[i].ctime = 0;
  }
}
#else
void taucs_profile_report() {}
#endif /* TAUCS_CONFIG_PROFILING */

#ifdef OSTYPE_win32
#define TAUCS_TIMER

#include <windows.h>

double taucs_wtime() {
  double wtime;

  SYSTEMTIME systime;

  GetSystemTime(&systime);

  wtime = 0.0
    +  0.001 * (double) systime.wMilliseconds
    +    1.0 * (double) systime.wSecond
    +   60.0 * (double) systime.wMinute
    + 3600.0 * (double) systime.wHour;

  return wtime; 
}
double taucs_ctime() { 
  double ctime;
  FILETIME creationt,exitt,kernel,user;
  ULARGE_INTEGER ukernel,uuser;
  HANDLE self;

  self = GetCurrentProcess();

  if (!GetProcessTimes(self,
		       &creationt,
		       &exitt,
		       &kernel,
		       &user))
    return 0.0;

  ukernel.LowPart  = kernel.dwLowDateTime;
  ukernel.HighPart = kernel.dwHighDateTime;
  uuser.LowPart  = user.dwLowDateTime;
  uuser.HighPart = user.dwHighDateTime;
  
  ctime = ((double) (signed __int64) ukernel.QuadPart / 10000000.0)
        + ((double) (signed __int64) uuser.QuadPart   / 10000000.0);

  CloseHandle( self );

  return ctime;
}
#endif /* win32 */

/*#include "taucs.h"*/

/* Return time in nanoseconds */
 
#if 0
#ifdef OSTYPE_linux_not_reliable
#define TAUCS_TIMER

#include <stdio.h>                                                 
#include <unistd.h>
#include <sys/types.h>                                                 
#include <sys/timeb.h>                                                 


/* p5tsc.h -- functions to use Pentium cycle counter for timing of events.
   Christian Kurmann <kurmann@inf.ethz.ch>
   based on Brad Karp, David Mazieres's p5cnt package from Harvard. */

typedef unsigned long uint32;

/* Cycle Counter */

/*
 *  Write <hi>:<lo> into MSR number <msr>.
 */

__inline__ void
wrmsr (const uint32 msr, uint32 hi, uint32 lo)
{
  __asm __volatile (
		    /*
		      "movl %0, %%ecx         # MSR to be written
		      movl %1, %%edx          # High order 32 bits
		      movl %2, %%eax          # Low order 32 bits
		      .byte 0xf; .byte 0x30   # WRMSR instruction"
		    */
        "movl %0, %%ecx movl %1, %%edx movl %2, %%eax .byte 0xf; .byte 0x30 # WRMSR instr"
    : : "g" (msr), "g" (hi), "g" (lo) : "eax", "ecx", "edx");
}

/* macro for clearing tsc */
#define cltsc wrmsr((uint32) 0x10, (uint32) (0), (uint32) (0))

/*
 *  Read 64 bit time stamp counter.  Put the high 32 bits in
 *  <*hi>, and the lower 32 bits in <*lo>.
 */
__inline__ void
rdtsc (uint32 *hi, uint32 *lo)
{
  __asm __volatile (
		    /*
		      ".byte 0xf; .byte 0x31  # RDTSC instruction
		      movl %%edx, %0          # High order 32 bits
		      movl %%eax, %1          # Low order 32 bits"
		    */
      ".byte 0xf; .byte 0x31 movl %%edx, %0 movl %%eax, %1 # RDTSC instruction"
    : "=g" (*hi), "=g" (*lo) :: "eax", "edx");
}

/* Performance Monitor Counters */

__inline__ void
spmc (uint32 ecx)
{
  __asm __volatile (
        "movl %0, %%ecx         # select counter "
    : : "g" (ecx) : "ecx");
}

/*
 *  Read 64 bit Performance Monitor Counter.  Put the high 32 bits in
 *  <*hi>, and the lower 32 bits in <*lo>.
 */
__inline__ void
rdpmc (uint32 *hi, uint32 *lo)
{
  __asm __volatile (
		    /*
		      ".byte 0xf; .byte 0x33  # RDPMC instruction
		      movl %%edx, %0          # High order 32 bits
		      movl %%eax, %1          # Low order 32 bits"
		    */
    ".byte 0xf; .byte 0x33 movl %%edx, %0 movl %%eax, %1 # RDPMC instruction"
    : "=g" (*hi), "=g" (*lo) :: "eax", "edx");
}

/* 64 bit subtract t1-t0 (result 32 bit integer) */
int subtract64(uint32 hi0, uint32 lo0, uint32 hi1, uint32 lo1 )
{
  uint32 hir, lor;

  hir = (hi1 - hi0);
  lor = (lo1 - lo0);
  if (lo1 < lo0) hir -= 1;
  return (hir > 0 ? 0:lor);
}

double timer()
{
  uint32        hi, lo;
  static uint32 hi0, lo0;
  static uint32 hi1, lo1;
    uint32 hir, lor;
  static double loticks_per_ns;
  static double hiticks_per_ns;
  static int    first_time    = 1;

  if (first_time) {
    struct timeb T;
    static time_t start_time, time_diff;
    static time_t start_mill, mill_diff;
    int    rc;
    double dt;
    /*uint32 hi0, lo0, hi1, lo1;*/
    double ticks;

    first_time = 0;

    rc = ftime( &T );
    rdtsc(&hi0,&lo0);
    start_time = T.time;
    start_mill = T.millitm;

    sleep(1);

    rc = ftime( &T );
    rdtsc(&hi1,&lo1);
    time_diff = T.time - start_time;
    mill_diff = T.millitm - start_mill; 

    dt = (1e9) * ((double) time_diff) + (1e6) * ((double) mill_diff);

    hir = (hi1 - hi0);
    lor = (lo1 - lo0);
    if (lo1 < lo0) hir -= 1;
    ticks = (double) lor + (double) hir * 4294967296.0;

    loticks_per_ns = ticks/dt;
    hiticks_per_ns = ticks/(dt / 4294967296.0);

    hir = (hi1 - hi0);
    loticks_per_ns = (4294967296.0 * (double) hir + (double) lo1 - (double) lo0) / dt;

    log_printf ("timer: hi0 %u lo0 %u hi1 %u lo1 %u\n",hi0,lo0,hi1,lo1);
    log_printf ("timer: lo/ns %lg hi/ns %lg\n",loticks_per_ns,hiticks_per_ns);

    rdtsc(&hi0,&lo0);
  }

  rdtsc(&hi1,&lo1);

  hir = (hi1 - hi0);
  return ( loticks_per_ns * ( 4294967296.0 * (double) hir + (double) lo1 ) );

  /*
  rdtsc(&hi,&lo);
  return ( ((double) hi * hiticks_per_ns) + ((double) lo * loticks_per_ns) );
  */
}

#endif /* OSTYPE_linux_not_reliable */
#endif /* if 0 */

/*********************************************************/
/*                                                       */
/*********************************************************/

#ifndef OSTYPE_win32
#define TAUCS_TIMER

#include <stdio.h>                                                 
#include <unistd.h>

/*
  #ifndef OSTYPE_solaris
  #include <sys/time.h>
  #include <sys/resource.h>
  #endif
*/
#ifdef OSTYPE_solaris
#define _XPG4_2
#endif
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/types.h>                                                 
#include <sys/timeb.h>                                                 

double taucs_wtime()
{
  struct timeb T;
  /*static int first_time    = 1;*/
  /*  static time_t start_time, time_diff;
      static time_t start_mill, mill_diff;
  */

  static time_t time_diff;
  static time_t mill_diff;
  /*int    rc;*/
  double dt;
  
  (void) ftime( &T );
  /*
  if (first_time) {
    first_time = 0;
    start_time = T.time;
    start_mill = T.millitm;
  }

  time_diff = T.time - start_time;
  mill_diff = T.millitm - start_mill; 
  */
  time_diff = T.time;
  mill_diff = T.millitm;

  dt = ((double) time_diff) + (1e-3) * ((double) mill_diff);

  return dt;
}

double taucs_ctime()
{
  /*
  #ifdef OSTYPE_solaris
  return 0.0;
  #else
  */
  struct rusage a;
  
  getrusage(RUSAGE_SELF,&a);

  return (double) 
    (double) ((a.ru_utime).tv_sec +(a.ru_stime).tv_sec ) +
    (double) ((a.ru_utime).tv_usec+(a.ru_stime).tv_usec) * 1.0e-6;
  /*#endif*/
}

#endif /* not win32 */

/*********************************************************/
/*                                                       */
/*********************************************************/

#ifndef TAUCS_TIMER
#define TAUCS_TIMER
double taucs_wtime() { return 0.0; }
double taucs_ctime() { return 0.0; }

#endif

#if 0
void cpu_time_from_last(char s[])
{
  struct rusage a;
  struct timeb T;
  static int first_time    = 1;
  static time_t start_time, time_diff;
  static time_t start_mill, mill_diff;
  /*int    rc;*/
  double dt,cpu_t;
  static double last_cpu_t;

  (void) ftime( &T );

  if (first_time) {
    first_time = 0;
    start_time = T.time;
    start_mill = T.millitm;
    getrusage(RUSAGE_SELF,&a);
    
    last_cpu_t = (a.ru_utime).tv_sec+(a.ru_stime).tv_sec+
      ((a.ru_utime).tv_usec+(a.ru_stime).tv_usec)/1000000.0; 
      

    taucs_printf("starting timer\n");
  }
  else
    {
      time_diff = T.time - start_time;
      mill_diff = T.millitm - start_mill; 
      
      dt = ((double) time_diff) + ((double) mill_diff)/1000.;

      start_time = T.time;
      start_mill = T.millitm;
      
      getrusage(RUSAGE_SELF,&a);

      cpu_t = (a.ru_utime).tv_sec+(a.ru_stime).tv_sec+
	((a.ru_utime).tv_usec+(a.ru_stime).tv_usec)/1000000.0; 
      
      taucs_printf("%s - CPU Time from last time : %lf\n", s, cpu_t-last_cpu_t);
      taucs_printf("%s - Wall Clock Time from last time : %lf\n",s,dt);
      
      last_cpu_t = cpu_t;
    }
}

void cpu_time_from_start(char s[])
{
  struct rusage a;
  struct timeb T;
  static int first_time    = 1;
  static time_t start_time, time_diff;
  static time_t start_mill, mill_diff;
  /*int    rc;*/
  double dt,cpu_t;
  static double start_cpu_t;
  
  (void) ftime( &T );

  if (first_time) {
    first_time = 0;
    start_time = T.time;
    start_mill = T.millitm;
    getrusage(RUSAGE_SELF,&a);

    start_cpu_t = (a.ru_utime).tv_sec+(a.ru_stime).tv_sec+
      ((a.ru_utime).tv_usec+(a.ru_stime).tv_usec)/1000000.0; 
      

    taucs_printf("starting timer\n");
  }
  else
    {
      time_diff = T.time - start_time;
      mill_diff = T.millitm - start_mill; 
      
      dt = ((double) time_diff) + ((double) mill_diff)/1000.;
      
      getrusage(RUSAGE_SELF,&a);

      cpu_t = (a.ru_utime).tv_sec+(a.ru_stime).tv_sec+
	((a.ru_utime).tv_usec+(a.ru_stime).tv_usec)/1000000.0; 
      
      taucs_printf("%s - CPU Time from beginning : %lf\n",s,cpu_t-start_cpu_t);
      taucs_printf("%s - Wall Clock Time from beginning : %lf\n",s,dt);
      
    }
}
#endif /* if 0 */

#endif /* TAUCS_CONFIG_TIMING */

/*********************************************************/
/*                                                       */
/*********************************************************/
